use crate::bpf_rs;
use crate::common::DeviceType;
use crate::common::LocalSrv6SidConfig;
use serde::{Deserialize, Serialize};
use std::net::Ipv6Addr;

#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Srv6Segments(Vec<Option<Ipv6Addr>>);

impl Srv6Segments {
    pub fn as_vec(&self) -> &Vec<Option<Ipv6Addr>> {
        &self.0
    }
}

impl From<Vec<Option<Ipv6Addr>>> for Srv6Segments {
    fn from(value: Vec<Option<Ipv6Addr>>) -> Self {
        Self(value)
    }
}

impl From<&Srv6Segments> for bpf_rs::Srv6Segments {
    fn from(value: &Srv6Segments) -> Self {
        let mut segments = [bpf_rs::Ipv6Addr8::default(); bpf_rs::SRV6_SEGMENT_LIST_MAX_LENGTH];
        for (i, segment) in value.0.iter().enumerate() {
            if i == bpf_rs::SRV6_SID_CONFIGS_MAX_LENGTH {
                break;
            }
            segments[i] = segment.into();
        }
        segments
    }
}

impl From<Srv6Segments> for bpf_rs::Srv6Segments {
    fn from(value: Srv6Segments) -> Self {
        Self::from(&value)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Route {
    pub segments: Srv6Segments,
}

impl From<&Route> for bpf_rs::Srv6Route {
    fn from(value: &Route) -> Self {
        Self {
            segments: bpf_rs::Srv6Segments::from(&value.segments),
        }
    }
}

impl From<Route> for bpf_rs::Srv6Route {
    fn from(value: Route) -> Self {
        Self::from(&value)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct KeyValue<K, V> {
    pub key: K,
    pub value: V,
}

pub type RouteKeyValue = KeyValue<cidr::IpInet, Route>;
pub type LocalSidConfigKeyValue = KeyValue<Ipv6Addr, LocalSrv6SidConfig>;

impl From<LocalSidConfigKeyValue> for bpf_rs::srv6::router::LocalSidConfigKeyValue {
    fn from(value: LocalSidConfigKeyValue) -> Self {
        Self {
            key: value.key.into(),
            value: value.value.into(),
        }
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Config {
    pub local_sid_configs: Vec<LocalSidConfigKeyValue>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub routes: Vec<RouteKeyValue>,
}

impl From<Config> for bpf_rs::srv6::router::Config {
    fn from(value: Config) -> Self {
        let mut ipv4_routes = Vec::new();
        let mut ipv6_routes = Vec::new();

        for kv in value.routes {
            match kv.key {
                cidr::IpInet::V4(key) => {
                    ipv4_routes.push(bpf_rs::srv6::router::Ipv4RouteKeyValue {
                        key: key.into(),
                        value: kv.value.into(),
                    })
                }
                cidr::IpInet::V6(key) => {
                    ipv6_routes.push(bpf_rs::srv6::router::Ipv6RouteKeyValue {
                        key: key.into(),
                        value: kv.value.into(),
                    })
                }
            }
        }

        Self {
            local_sid_configs: value
                .local_sid_configs
                .into_iter()
                .map(|x| x.into())
                .collect(),
            ipv4_routes,
            ipv6_routes,
        }
    }
}

pub struct Router {
    inner: bpf_rs::srv6::router::Router,
}

impl Router {
    pub fn new(iface: String, device_type: DeviceType) -> Self {
        Self {
            inner: bpf_rs::srv6::router::Router::new(iface, device_type.into()),
        }
    }

    pub fn get_iface(&self) -> &str {
        self.inner.iface()
    }

    pub fn attach(&self, config: Config) -> Result<(), anyhow::Error> {
        self.inner.attach(config.into())
    }

    pub fn detach_and_destroy(&self) -> Result<(), anyhow::Error> {
        self.inner.detach_and_destroy()
    }
}
